# coding=utf-8
from socket import *
import os
import re
import multiprocessing
from webs.service import routing_service, config_service
from utils import date_util

BASE_PATH = os.path.dirname(os.path.abspath(__file__))


class WebServer:
    def __init__(self, server_port, mydict):
        # 创建套接字
        self.server_socket = socket(AF_INET, SOCK_STREAM)
        # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放，这样就保证了，下次运行程序时 可以立即绑定7788端口
        self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        # 设置服务端提供服务的端口号
        self.server_socket.bind(('', server_port))
        # 使用socket创建的套接字默认的属性是主动的，使用listen将其改为被动，用来监听连接
        self.server_socket.listen(128)  # 最多可以监听128个连接
        self.mydict = mydict

    def start_http_service(self):
        # 开启while循环处理访问过来的请求
        while True:
            client_socket, clientAddr = self.server_socket.accept()
            if BASE_PATH.startswith('/'):  # linux系统
                t1 = date_util.get_timestamp_now()
                new_process = multiprocessing.Process(target=self.handle_client, args=(client_socket, self.mydict))
                new_process.start()  # 开启子进程
                t2 = date_util.get_timestamp_now()
                if t2 - t1 > 0:
                    print("页面调用时间超过1s")
            else:
                self.handle_client(client_socket, self.mydict)
            client_socket.close()

    def handle_client(self, client_socket, mydict):
        """为一个客户端服务"""
        # 接收对方发送的数据
        recv_data = client_socket.recv(1024).decode("utf-8")  # 1024表示本次接收的最大字节数
        # 打印从客户端发送过来的数据内容
        request_header_lines = recv_data.splitlines()
        # 使用正则匹配出文件路径
        if len(request_header_lines) == 0:
            print(':::::请求资源为空')
            response_headers = "HTTP/1.1 404 not found\r\n"  # 200 表示找到这个资源
            response_headers += "\r\n"  # 空一行与body隔开
            response_body = "<h1>sorry,file not found</h1>"
            response = response_headers + response_body
            client_socket.send(response.encode("utf-8"))
            return
        ret = re.match(r"[^/]+/([^\s]*)", request_header_lines[0])
        # 判断file_path是否py文件后缀，如果是则请求动态资源，否则请求静态资源
        if ret.group(1).__contains__("."):
            # 请求静态资源
            try:
                static_path = 'assets' + ret.group(1).split('assets')[1]
                # 设置返回的头信息 header
                response_headers = "HTTP/1.1 200 OK\r\n"  # 200 表示找到这个资源
                response_headers += "\r\n"  # 空一行与body隔开
                # 读取静态资源
                file_name = os.path.join(BASE_PATH, 'html', static_path)  # 设置读取的文件路径
                key_name = ret.group(1).replace('\\', '').replace('/', '').replace('.', '').replace('-', '')
                if key_name not in mydict.keys():  # 增加缓存
                    if file_name.__contains__("?v"):
                        file_name = file_name.split('?v')[0]
                    if file_name == 'assets/css/bootstrap.min.css.map':  # 不影响
                        mydict[key_name] = ''
                    else:
                        f = open(file_name, "rb")  # 以二进制读取文件内容
                        mydict[key_name] = f.read()
                        f.close()
                response_body = mydict[key_name]
                client_socket.send(response_headers.encode("utf-8"))  # 转码utf-8并send数据到浏览器
                client_socket.send(response_body)  # 转码utf-8并send数据到浏览器
            except:
                print("#######静态请求错误" + ret.group(1))
        else:
            try:
                # 获取动态页面
                self.start_response('200 OK', [('Content-Type', 'text/html;charset=UTF-8')])
                params_dict = {}
                params_dict = self.post_info(params_dict, request_header_lines)
                params_dict = self.header(ret.group(1), request_header_lines, params_dict)
                # 路由页面
                response_body = routing_service.route(ret.group(1), params_dict)
                client_socket.send(params_dict['response_headers'].encode("utf-8"))
                client_socket.send(response_body)
            except:
                print("*******动态请求错误" + ret.group(1))

    def start_response(self, status, header):
        self.application_header = [status, header]

    # 获取post请求信息
    def post_info(self, params_dict, request_header_lines):
        method = request_header_lines[0].split(' ')[0]
        params_dict['method'] = method
        if method == 'POST' or method == 'post':  # 拼接post参数
            params_str = request_header_lines[len(request_header_lines) - 1]
            params_list = params_str.split('&')
            for param_str in params_list:
                params = param_str.split('=')
                params_dict[params[0]] = params[1]
        return params_dict

    def header(self, url, request_header_lines, params_dict):
        response_headers = "HTTP/1.1 " + self.application_header[0] + "\r\n"
        for var in self.application_header[1]:
            response_headers += var[0] + ":" + var[1] + "\r\n"
        for line in request_header_lines:  # 放入Cookie
            line_list = line.split(':')
            if line_list[0] == 'Cookie':
                Cookies = line_list[1]
                Cookies_list = Cookies.split(';')
                for cok in Cookies_list:
                    # if cok != '':  # 放入请求头
                    #     response_headers += "Set-Cookie: {}\r\n".format(cok)
                    cookie_list = cok.split('=')
                    if len(cookie_list) == 2:
                        params_dict[cookie_list[0].replace(' ', '')] = cookie_list[1].replace(' ', '')  # cookie 放入参数中
        if url == config_service.url3:  # 登录信息放入 cookie
            cok = config_service.username + '=' + params_dict[config_service.username]
            response_headers += "Set-Cookie: {}\r\n".format(cok)
        response_headers += "\r\n"
        params_dict['response_headers'] = response_headers
        return params_dict


def main():
    # 通过sys.argv来获取服务端的端口号
    # server_port = int(sys.argv[1])
    server_port = int('8888')
    mydict = multiprocessing.Manager().dict()  # 主进程与子进程共享这个字典
    webserver = WebServer(server_port, mydict)
    webserver.start_http_service()


if __name__ == "__main__":
    main()
